import tkinter as tk
from tkinter import messagebox, scrolledtext
import re
import cProfile
import pstats
from io import StringIO
import unittest
from unittest.mock import mock_open, patch


# ---------------------- 新增：独立统计函数（便于单元测试） ----------------------
def count_chars(content):
    """统计字符数（排除换行符、回车符）"""
    content_clean = content.replace('\n', '').replace('\r', '')
    return len(content_clean)


def count_words(content):
    """统计纯字母单词数（排除标点、数字、符号）"""
    content_clean = content.replace('\n', '').replace('\r', '')
    words = re.findall(r'\b[a-zA-Z]+\b', content_clean)
    return len(words)


def count_sentences(content):
    """统计句子数（排除缩写点，以.?!结尾且前后无单词内点）"""
    content_clean = content.replace('\n', '').replace('\r', '').strip()
    if not content_clean:
        return 0
    # 正则：匹配不以单词内点（如Mr.）结尾，以.?!结尾的分割位置
    sentences = re.split(r'(?<!\w\.)(?<=\.|\?|!)\s*', content_clean)
    return len([s for s in sentences if s.strip()])


def count_code_lines(lines):
    """统计代码行（有效代码行、空行、注释行）"""
    valid = 0
    empty = 0
    comment = 0
    for line in lines:
        stripped_line = line.strip()
        if not stripped_line:
            empty += 1
        elif stripped_line.startswith(("#", "//")):
            comment += 1
        else:
            valid += 1
    return valid, empty, comment


# ---------------------- 新增：单元测试类 ----------------------
class TestTextStatistics(unittest.TestCase):
    """文本统计功能单元测试用例，覆盖正常/边界/异常场景"""

    # 1. 字符统计测试
    def test_count_chars_normal(self):
        """正常场景：含字母、符号、换行符"""
        self.assertEqual(count_chars("Hello! 123\nPython测试"), 16)  # Hello!123Python测试共16字符

    def test_count_chars_empty(self):
        """边界场景：空字符串、纯换行符"""
        self.assertEqual(count_chars(""), 0)
        self.assertEqual(count_chars("\n\r\n"), 0)

    # 2. 单词统计测试
    def test_count_words_normal(self):
        """正常场景：纯字母单词+标点分隔"""
        self.assertEqual(count_words("Hello, world! Test Python123"), 3)  # Hello、world、Test

    def test_count_words_no_alpha(self):
        """边界场景：无纯字母单词（数字、符号、中文）"""
        self.assertEqual(count_words("123! @# 你好，测试"), 0)

    # 3. 句子统计测试
    def test_count_sentences_normal(self):
        """正常场景：含缩写（Mr.）和多句尾符号"""
        self.assertEqual(count_sentences("Mr. Li said: 'Hello!' How are you? Fine."), 3)

    def test_count_sentences_empty(self):
        """边界场景：空内容、无句尾符号"""
        self.assertEqual(count_sentences(""), 0)
        self.assertEqual(count_sentences("Hello world 123"), 0)

    # 4. 代码行统计测试
    def test_count_code_lines_mixed(self):
        """正常场景：混合有效行、空行、注释行"""
        test_lines = [
            "# 这是Python注释",
            "print('Hello')",  # 有效行
            "",  # 空行
            "// 这是C++注释",
            "x = 1 + 2",  # 有效行
            "   ",  # 空行（仅空格）
            "def func():"  # 有效行
        ]
        valid, empty, comment = count_code_lines(test_lines)
        self.assertEqual(valid, 3)
        self.assertEqual(empty, 2)
        self.assertEqual(comment, 2)

    # 5. 文件读取逻辑测试（Mock模拟）
    @patch('builtins.open', new_callable=mock_open, read_data="Hello! This is a test file.")
    def test_file_reading(self, mock_file):
        """验证文件读取路径和内容正确性"""
        file_path = r"D:\code\two\拓展功能测试.txt"
        with open(file_path, "r", encoding="utf-8") as f:
            content = f.read()
        # 验证读取内容正确
        self.assertEqual(content, "Hello! This is a test file.")
        # 验证文件路径和参数正确
        mock_file.assert_called_once_with(file_path, "r", encoding="utf-8")


# ---------------------- 主GUI功能 ----------------------
def text_statistics_gui():
    # 创建主窗口
    root = tk.Tk()
    root.title("文本统计工具（含代码行统计+单元测试）")
    root.geometry("700x550")
    root.resizable(False, False)

    # 初始化统计变量
    str_count = tk.IntVar(value=0)     # 字符数
    word_count = tk.IntVar(value=0)    # 单词数
    sen_count = tk.IntVar(value=0)     # 句子数
    code_valid_lines = tk.IntVar(value=0)  # 有效代码行
    code_empty_lines = tk.IntVar(value=0)  # 空行数
    code_comment_lines = tk.IntVar(value=0)# 注释行数

    file_path = r"D:\code\two\拓展功能测试.txt"

    # 整合统计逻辑（调用独立函数）
    def count_all_dimensions():
        try:
            with open(file_path, "r", encoding="utf-8") as fp:
                content = fp.read()
                lines = content.splitlines()

                # 调用独立统计函数
                char_total = count_chars(content)
                final_word_count = count_words(content)
                final_sen_count = count_sentences(content)
                valid, empty, comment = count_code_lines(lines)

                # 更新界面变量
                str_count.set(char_total)
                word_count.set(final_word_count)
                sen_count.set(final_sen_count)
                code_valid_lines.set(valid)
                code_empty_lines.set(empty)
                code_comment_lines.set(comment)

        except FileNotFoundError:
            messagebox.showerror("错误", f"文件未找到，请检查路径：{file_path}")
        except Exception as e:
            messagebox.showerror("错误", f"读取文件失败：{str(e)}")

    # 程序启动时自动统计
    def auto_count():
        count_all_dimensions()

    # 性能测试函数
    def run_performance_test():
        pr = cProfile.Profile()
        pr.enable()
        for _ in range(100):
            count_all_dimensions()
        pr.disable()

        s = StringIO()
        sortby = 'cumulative'
        ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
        ps.print_stats(20)

        # 性能结果窗口
        result_window = tk.Toplevel(root)
        result_window.title("性能测试结果")
        result_window.geometry("800x600")

        scroll = tk.Scrollbar(result_window)
        scroll.pack(side=tk.RIGHT, fill=tk.Y)

        result_text = tk.Text(result_window, wrap=tk.NONE, yscrollcommand=scroll.set)
        result_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scroll.config(command=result_text.yview)

        result_text.insert(tk.END, s.getvalue())
        result_text.config(state=tk.DISABLED)

    # 新增：运行单元测试并展示结果
    def run_unit_tests():
        """执行所有单元测试，在独立窗口显示详细结果"""
        # 捕获测试输出
        test_stream = StringIO()
        runner = unittest.TextTestRunner(stream=test_stream, verbosity=2)
        suite = unittest.TestLoader().loadTestsFromTestCase(TestTextStatistics)
        test_result = runner.run(suite)

        # 测试结果窗口
        test_window = tk.Toplevel(root)
        test_window.title("单元测试结果")
        test_window.geometry("800x600")
        test_window.resizable(False, False)

        # 滚动文本框（显示详细日志）
        result_text = scrolledtext.ScrolledText(
            test_window,
            wrap=tk.WORD,
            font=("Consolas", 10),
            state=tk.DISABLED
        )
        result_text.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # 插入测试日志
        result_text.config(state=tk.NORMAL)
        result_text.insert(tk.END, test_stream.getvalue())

        # 插入测试汇总（成功/失败/错误数）
        summary = f"\n{'='*50}\n测试汇总：\n"
        summary += f"总用例数：{test_result.testsRun}\n"
        summary += f"成功：{test_result.testsRun - len(test_result.failures) - len(test_result.errors)}\n"
        summary += f"失败：{len(test_result.failures)}\n"
        summary += f"错误：{len(test_result.errors)}\n"
        result_text.insert(tk.END, summary)
        result_text.config(state=tk.DISABLED)

    # ---------------------- 界面组件 ----------------------
    # 标题
    tk.Label(root, text="文本统计工具（含代码行统计+单元测试）", font=("微软雅黑", 14, "bold")).pack(pady=20)

    # 1. 文本基础统计框架
    text_frame = tk.Frame(root, bd=2, relief=tk.GROOVE, padx=20, pady=10)
    text_frame.pack(fill=tk.X, padx=50, pady=5)

    tk.Label(text_frame, text="拓展功能测试.txt 文本统计：", font=("微软雅黑", 11, "bold")).grid(row=0, column=0, columnspan=2, pady=5, sticky=tk.W)
    tk.Label(text_frame, text="字符数：", font=("微软雅黑", 10)).grid(row=1, column=0, padx=30, pady=3, sticky=tk.W)
    tk.Label(text_frame, textvariable=str_count, font=("微软雅黑", 10), width=15, bg="#e0e0e0").grid(row=1, column=1, pady=3, sticky=tk.W)
    tk.Label(text_frame, text="单词数：", font=("微软雅黑", 10)).grid(row=2, column=0, padx=30, pady=3, sticky=tk.W)
    tk.Label(text_frame, textvariable=word_count, font=("微软雅黑", 10), width=15, bg="#e0e0e0").grid(row=2, column=1, pady=3, sticky=tk.W)
    tk.Label(text_frame, text="句子数：", font=("微软雅黑", 10)).grid(row=3, column=0, padx=30, pady=3, sticky=tk.W)
    tk.Label(text_frame, textvariable=sen_count, font=("微软雅黑", 10), width=15, bg="#e0e0e0").grid(row=3, column=1, pady=3, sticky=tk.W)

    # 2. 代码行统计框架
    code_frame = tk.Frame(root, bd=2, relief=tk.GROOVE, padx=20, pady=10)
    code_frame.pack(fill=tk.X, padx=50, pady=10)

    tk.Label(code_frame, text="拓展功能测试.txt 代码行统计：", font=("微软雅黑", 11, "bold")).grid(row=0, column=0, columnspan=2, pady=5, sticky=tk.W)
    tk.Label(code_frame, text="有效代码行：", font=("微软雅黑", 10)).grid(row=1, column=0, padx=30, pady=3, sticky=tk.W)
    tk.Label(code_frame, textvariable=code_valid_lines, font=("微软雅黑", 10), width=15, bg="#e0e0e0").grid(row=1, column=1, pady=3, sticky=tk.W)
    tk.Label(code_frame, text="空行数：", font=("微软雅黑", 10)).grid(row=2, column=0, padx=30, pady=3, sticky=tk.W)
    tk.Label(code_frame, textvariable=code_empty_lines, font=("微软雅黑", 10), width=15, bg="#e0e0e0").grid(row=2, column=1, pady=3, sticky=tk.W)
    tk.Label(code_frame, text="注释行数：", font=("微软雅黑", 10)).grid(row=3, column=0, padx=30, pady=3, sticky=tk.W)
    tk.Label(code_frame, textvariable=code_comment_lines, font=("微软雅黑", 10), width=15, bg="#e0e0e0").grid(row=3, column=1, pady=3, sticky=tk.W)

    # 操作按钮（新增单元测试按钮）
    btn_frame = tk.Frame(root, pady=15)
    btn_frame.pack()
    tk.Button(btn_frame, text="刷新统计", command=auto_count, width=12).grid(row=0, column=0, padx=10)
    tk.Button(btn_frame, text="性能测试", command=run_performance_test, width=12, bg="#4CAF50", fg="white").grid(row=0, column=1, padx=10)
    tk.Button(btn_frame, text="单元测试", command=run_unit_tests, width=12, bg="#2196F3", fg="white").grid(row=0, column=2, padx=10)  # 新增测试按钮
    tk.Button(btn_frame, text="退出", command=root.quit, width=12).grid(row=0, column=3, padx=10)

    # 启动时自动统计
    auto_count()

    root.mainloop()


if __name__ == "__main__":
    text_statistics_gui()